package com.gly091020.util;

import com.gly091020.BilibiliMedia;
import com.sapher.youtubedl.YoutubeDL;
import com.sapher.youtubedl.YoutubeDLException;
import com.sapher.youtubedl.YoutubeDLRequest;
import com.sapher.youtubedl.YoutubeDLResponse;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.watermedia.api.network.patchs.AbstractPatch;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Arrays;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class BilibiliPatch extends AbstractPatch {
    @Override
    public String platform() {
        return "bilibili";
    }

    @Override
    public boolean isValid(URI uri) {
        if(!BilibiliMedia.config.enable){return false;}
        return uri.toString().contains("b23.tv") || uri.toString().contains("bilibili.com");
    }

    @Override
    public Result patch(URI uri, Quality prefQuality) throws FixingURLException {
        super.patch(uri, prefQuality);
        uri = URI.create(extractUrl(uri.toString()));

        var dl = BilibiliMediaUtil.tryGetLocalFile(uri.toString());
        if(dl != null){
            return new Result(URI.create(dl), false, false);
        }

        UUID videoUUID = UUID.randomUUID();

        if(BilibiliMedia.config.useBBDown){
            patchWithBBDown(uri, videoUUID);
        }else{
            patchWithYoutubeDL(uri, prefQuality, videoUUID);
        }

        File outFile;
        var l = BilibiliMediaUtil.getDownloadPath().toFile().listFiles((dir, name) -> name.contains(videoUUID.toString()));
        if(l == null || l.length == 0){
            BilibiliMedia.LOGGER.error("下载成功，但找不到文件");
            throw new FixingURLException(uri, new RuntimeException("下载成功但找不到文件"));
        }
        outFile = Arrays.asList(l).get(0);
        BilibiliMediaUtil.updateVideoFile(uri.toString(), outFile);
        return new Result(BilibiliMediaUtil.getUri(outFile), false, false);
    }

    public static String extractUrl(String inputUrl) throws FixingURLException {
        Pattern pattern = Pattern.compile("https?://(?:www\\.bilibili\\.com/video/BV\\w+|b23\\.tv/\\w+)", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(inputUrl);

        if (matcher.find()) {
            return matcher.group(0);
        } else {
            throw new FixingURLException(inputUrl, new RuntimeException("url is sus"));
        }
    }

    private static void patchWithYoutubeDL(URI uri, Quality prefQuality, UUID videoUUID) throws FixingURLException {
        YoutubeDLRequest request = getYoutubeDLRequest(uri, prefQuality, videoUUID);
        YoutubeDLResponse response;
        try {
            response = YoutubeDL.execute(request);
        } catch (YoutubeDLException e) {
            BilibiliMedia.LOGGER.error("执行时出现错误，可能没有安装yt_dlp", e);
            throw new FixingURLException(uri, e);
        }
        if(response.getExitCode() != 0){
            BilibiliMedia.LOGGER.debug("下载失败，程序运行返回：{}", response.getOut());
            throw new FixingURLException(uri, new RuntimeException("下载失败：程序返回值不为0"));
        }
    }

    private static void patchWithBBDown(URI uri, UUID videoUUID) throws FixingURLException {
        try {
            int exitCode = getExitCode(uri, videoUUID);
            BilibiliMedia.LOGGER.info("程序以{}退出", exitCode);
            if(exitCode != 0){
                throw new FixingURLException(uri, new RuntimeException("BBDown下载失败"));
            }

        } catch (IOException | InterruptedException e) {
            BilibiliMedia.LOGGER.error("BBDown下载失败", e);
            throw new FixingURLException(uri, new RuntimeException("BBDown下载失败"));
        }
    }

    private static int getExitCode(URI uri, UUID videoUUID) throws IOException, InterruptedException {
        ProcessBuilder builder = new ProcessBuilder(
                "BBDown",
                uri.toString(),
                "--work-dir", BilibiliMediaUtil.getDownloadPath().toString(),
                "-F", videoUUID.toString(),
                "-M", videoUUID.toString(),
                "--skip-cover",
                "--skip-subtitle",
                "-p", "1");
        // 懒得写视频分辨率解析了，反正WaterMedia又不给

        // 设置工作目录
        builder.directory(BilibiliMediaUtil.getDownloadPath().toFile());

        Process process = builder.start();

        return process.waitFor();
    }

    private static @NotNull YoutubeDLRequest getYoutubeDLRequest(URI uri, @Nullable Quality prefQuality, UUID videoUUID) {
        YoutubeDLRequest request = new YoutubeDLRequest(uri.toString(), BilibiliMediaUtil.getDownloadPath().toString());
        request.setOption("output", videoUUID.toString() + ".%(ext)s");
        request.setOption("playlist-items", "1");

        var h = 114514;
        if (prefQuality != null) {
            switch (prefQuality){
                case LOW -> h = 480;
                case LOWEST -> h = 360;
                case MIDDLE -> h = 720;
                case HIGH -> h = 1080;
            }
        }

        request.setOption("format", "bv*[height<=" + h + "]+ba/bestaudio");
        return request;
    }
}
